/**
 * @file CircBuff.h
 *
 * This file defines the circular buffer.
 *
 * @author Luc Fourestier
 * @date 2009
 */

#ifndef CIRCBUFF_H_
#define CIRCBUFF_H_

#include <pthread.h>
#include <semaphore.h>

#define CIRCB_ERROR_CANNOT_ALLOCATE 1		//!< Cannot create/allocate certain objects necessary for the circular buffer
#define CIRCB_ERROR_BUFFER_FULL 	2		//!< Buffer is full, cannot write
#define CIRCB_ERROR_BUFFER_EMPTY 	3		//!< Buffer is empty, cannot read
#define CIRCB_ERROR_WAIT_KILLED 	4		//!< Signal that the wait loop has been killed
#define CIRCB_ERROR_BAD_PARAM    	5		//!< Parameter incorrect
#define CIRCB_ERROR_ELT_EMPTY    	6		//!< In watch, no element can be watch because it is empty
#define CIRCB_WARNING_END_OF_BUFF  	7		//!< In watch, the index specified is equal to the (writeIndex-1) (End of Buffer)
#define CIRCB_WARNING_BEG_OF_BUFF  	8		//!< In watch, the index specified is equal to the ReadIndex (Beginning of Buffer)
#define CIRCB_ERROR_TAG_EMPTY 		9		//!< No element to be tagged!
#define CIRCB_ERROR_ANA_EMPTY 		10		//!< No element to be analyzed!

// Timeout specific values
#define CIRCB_WAIT_FOREVER 	-1
#define CIRCB_NO_WAIT 		0

/**
 * The circular buffer info structure
 */
typedef struct CircBuff_s
{
	unsigned char Magic; 			//!< Magic number to prevent using unallocated CircBuff_t
	unsigned int MaxElementNumber; 	//!< Max number of element in the circular buffer
	unsigned int MaxElementSize;	//!< The max element size
	unsigned char * Buffer_p;		//!< Pointer to the beginning of the circular buffer
	unsigned int ElementCount;		//!< The current number of element in the buffer
	unsigned int * ElementSize;		//!< Table containing the actual size of each element in the buffer
	pthread_mutex_t CircBMutex;		//!< The mutex protecting buffer access

	unsigned int WriteIndex;		//!< The write index
	pthread_mutex_t WriteLock;		//!< The mutex protecting the write
	sem_t WriteAccess;				//!< Block the write, if the buffer is full
	unsigned char KillWrite;		//!< A boolean to kill the write waiting loop

	unsigned int ReadIndex;			//!< The read index
	pthread_mutex_t ReadLock;		//!< The mutex protecting the read
	sem_t ReadAccess;				//!< Block the read, if the buffer is empty
	unsigned char KillRead;			//!< A boolean to kill the read waiting loop

	unsigned int TagIndex;			//!< The tagging index
	pthread_mutex_t TagLock;		//!< The mutex protecting the tagging
	sem_t TagAccess;				//!< Block the tagging, if there is no element to be tagged
	unsigned char KillTag;			//!< A boolean to kill the tag waiting loop

	unsigned int AnalysisIndex;		//!< The Analysis index
	pthread_mutex_t AnalysisLock;	//!< The mutex protecting the analysis
	sem_t AnalysisAccess;			//!< Block the analysis, if there is no element to be analyzed
	unsigned char KillAnalysis;		//!< A boolean to kill the analysis waiting loop


} CircBuff_t;

/**
 * @brief Create a circular buffer
 */
int CircB_CreateCircBuff(CircBuff_t ** CircBuff_p, unsigned int MaxElementSize, unsigned int MaxElementNumber);

/**
 * @brief Delete circular buffer
 */
int CircB_DeleteCircBuff(CircBuff_t * CircBuff);

/**
 * @brief Get the write element pointer
 */
int CircB_GetWriteElt(CircBuff_t * CircBuff, void ** Buff_p, unsigned int * Size_p, int Timeout);

/**
 * @brief Validate the write element
 */
int CircB_ValidateWriteElt(CircBuff_t * CircBuff, unsigned int Size);

/**
 * @brief Release the write element
 */
int CircB_ReleaseWriteElt(CircBuff_t * CircBuff);

/**
 * @brief Kill waiting loop
 */
int CircB_KillWrite(CircBuff_t * CircBuff);

/**
 * @brief Get the read element pointer
 */
int CircB_GetReadElt(CircBuff_t * CircBuff, void ** Buff_p, unsigned int * Size_p, int Timeout);

/**
 * @brief Validate the read element
 */
int CircB_ValidateReadElt(CircBuff_t * CircBuff);

/**
 * @brief Release the read element
 */
int CircB_ReleaseReadElt(CircBuff_t * CircBuff);

/**
 * @brief Kill waiting loop
 */
int CircB_KillRead(CircBuff_t * CircBuff);

/**
 * @brief Get the tag element pointer
 */
int CircB_GetTagElt(CircBuff_t * CircBuff, void ** Buff_p, unsigned int * Size_p, int Timeout);

/**
 * @brief Validate the tag element
 */
int CircB_ValidateTagElt(CircBuff_t * CircBuff);

/**
 * @brief Release the tag element
 */
int CircB_ReleaseTagElt(CircBuff_t * CircBuff);

/**
 * @brief Kill waiting loop
 */
int CircB_KillTag(CircBuff_t * CircBuff);

/**
 * @brief Get the analysis element pointer
 */
int CircB_GetAnalysisElt(CircBuff_t * CircBuff, void ** Buff_p, unsigned int * Size_p, int Timeout);

/**
 * @brief Validate the analysis element
 */
int CircB_ValidateAnalysisElt(CircBuff_t * CircBuff);

/**
 * @brief Release the analysis element
 */
int CircB_ReleaseAnalysisElt(CircBuff_t * CircBuff);

/**
 * @brief Kill waiting loop
 */
int CircB_KillAnalysis(CircBuff_t * CircBuff);

#endif /* CIRCBUFF_H_ */
